#include "src/pbc/portable-bytecode-array.h"

#include <iomanip>

#include "src/pbc/portable-bytecode-array-iterator.h"
#include "src/pbc/portable-bytecode-decoder.h"

namespace portable {

// int BytecodeArray::SourcePosition(int offset) const {
//   int position = 0;
//   if (!HasSourcePositionTable()) return position;
//   for (SourcePositionTableIterator it(
//            source_position_table(kAcquireLoad),
//            SourcePositionTableIterator::kJavaScriptOnly,
//            SourcePositionTableIterator::kDontSkipFunctionEntry);
//        !it.done() && it.code_offset() <= offset; it.Advance()) {
//     position = it.source_position().ScriptOffset();
//   }
//   return position;
// }

// int BytecodeArray::SourceStatementPosition(int offset) const {
//   int position = 0;
//   if (!HasSourcePositionTable()) return position;
//   for (SourcePositionTableIterator it(source_position_table(kAcquireLoad));
//        !it.done() && it.code_offset() <= offset; it.Advance()) {
//     if (it.is_statement()) {
//       position = it.source_position().ScriptOffset();
//     }
//   }
//   return position;
// }

// void BytecodeArray::PrintJson(std::ostream& os) {
//   DisallowGarbageCollection no_gc;

//   Address base_address = GetFirstBytecodeAddress();
//   BytecodeArray handle_storage = *this;
//   Handle<BytecodeArray> handle(reinterpret_cast<Address*>(&handle_storage));
//    BytecodeArrayIterator iterator(handle);
//   bool first_data = true;

//   os << "{\"data\": [";

//   while (!iterator.done()) {
//     if (!first_data) os << ", ";
//     Address current_address = base_address + iterator.current_offset();
//     first_data = false;

//     os << "{\"offset\":" << iterator.current_offset() << ",
//     \"disassembly\":\"";  BytecodeDecoder::Decode(
//         os, reinterpret_cast<uint8_t*>(current_address), false);

//     if ( Bytecodes::IsJump(iterator.current_bytecode())) {
//       os << " (" << iterator.GetJumpTargetOffset() << ")";
//     }

//     if ( Bytecodes::IsSwitch(iterator.current_bytecode())) {
//       os << " {";
//       bool first_entry = true;
//       for ( JumpTableTargetOffset entry :
//            iterator.GetJumpTableTargetOffsets()) {
//         if (!first_entry) os << ", ";
//         first_entry = false;
//         os << entry.target_offset;
//       }
//       os << "}";
//     }

//     os << "\"}";
//     iterator.Advance();
//   }

//   os << "]";

//   int constant_pool_length = constant_pool()->length();
//   if (constant_pool_length > 0) {
//     os << ", \"constantPool\": [";
//     for (int i = 0; i < constant_pool_length; i++) {
//       Tagged<Object> object = constant_pool()->get(i);
//       if (i > 0) os << ", ";
//       os << "\"" << object << "\"";
//     }
//     os << "]";
//   }

//   os << "}";
// }

BytecodeArray::BytecodeArray(int length,
                             const std::vector<uint8_t>& raw_bytecodes,
                             int register_count, uint16_t parameter_count,
                             int exception_count, ConstantPool* constant_pool)
    : parameter_count_(parameter_count),
      register_count_(register_count),
      exception_count_(exception_count),
      bytecodes_size_(length),
      incoming_new_target_or_generator_register_(),
      raw_bytecodes_data_(raw_bytecodes),
      constant_pool_(constant_pool) {}

BytecodeArray::~BytecodeArray() {
  if (constant_pool_) delete constant_pool_;
}

int BytecodeArray::register_count() const { return register_count_; }

int BytecodeArray::exception_count() const { return exception_count_; }

int BytecodeArray::length() const { return bytecodes_size_; }

ConstantPool* BytecodeArray::constant_pool() { return constant_pool_; }

uintptr_t BytecodeArray::GetFirstBytecodeAddress() {
  return reinterpret_cast<uintptr_t>(raw_bytecodes_data_.data());
}

uint16_t BytecodeArray::parameter_count() const { return parameter_count_; }

uint16_t BytecodeArray::parameter_count_without_receiver() const {
  return parameter_count() - 1;
}

void BytecodeArray::set_incoming_new_target_or_generator_register(
    Register val) {
  incoming_new_target_or_generator_register_ = val;
}

Register BytecodeArray::incoming_new_target_or_generator_register() const {
  return incoming_new_target_or_generator_register_;
}

// static
void BytecodeArray::Disassemble(BytecodeArray* source, std::ostream& os) {
  os << "Parameter count: " << source->parameter_count() << "\n";
  os << "Register count: " << source->register_count() << "\n";
  os << "New target or Generator: r"
     << source->incoming_new_target_or_generator_register().index() << "\n";

  Address base_address = source->GetFirstBytecodeAddress();
  //   SourcePositionTableIterator
  //   source_positions(source->SourcePositionTable());

  BytecodeArrayIterator iterator(source);
  while (!iterator.done()) {
    os << "         ";

    Address current_address = base_address + iterator.current_offset();
    os << reinterpret_cast<const void*>(current_address) << " @ "
       << std::setw(4) << iterator.current_offset() << " : ";
    BytecodeDecoder::Decode(os, reinterpret_cast<uint8_t*>(current_address));
    if (Bytecodes::IsJump(iterator.current_bytecode())) {
      Address jump_target = base_address + iterator.GetJumpTargetOffset();
      os << " (" << reinterpret_cast<void*>(jump_target) << " @ "
         << iterator.GetJumpTargetOffset() << ")";
    }
    if (Bytecodes::IsSwitch(iterator.current_bytecode())) {
      os << " {";
      bool first_entry = true;
      for (JumpTableTargetOffset entry : iterator.GetJumpTableTargetOffsets()) {
        if (first_entry) {
          first_entry = false;
        } else {
          os << ",";
        }
        os << " " << entry.case_value << ": @" << entry.target_offset;
      }
      os << " }";
    }
    os << std::endl;
    iterator.Advance();
  }

  os << "Constant pool (size = " << source->constant_pool()->length() << ")\n";
  if (source->constant_pool()->length() > 0) {
    source->constant_pool()->Print(os);
  }

  os << "Handler Table (size = " << source->exception_count() << ")\n";
}

}  // namespace portable
